package com.example.project3.filter;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.example.project3.util.MyJWT;
import com.example.project3.util.ThreadLocalToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName TokenFilter .java
 * @Description empty
 * @Author LiuJinyu
 * @Date 2023/5/31 15:02
 * @Version 1.0
 */
@WebFilter("/**")
@Component
@Slf4j
public class TokenFilter implements Filter {
    /**
     * 路径白名单
     */
    private final static Set<String> writeOrder = new HashSet<>();

    private final MyJWT myJWT;

    private final RedisTemplate<String, Object> redisTemplate;

    private final ThreadLocalToken threadLocalToken;

    @Autowired
    public TokenFilter(MyJWT myJWT, RedisTemplate<String, Object> redisTemplate, ThreadLocalToken threadLocalToken) {
        this.myJWT = myJWT;
        this.redisTemplate = redisTemplate;
        this.threadLocalToken = threadLocalToken;
    }


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        writeOrder.add("/user/sendCode");
        writeOrder.add("/user/verification");
        writeOrder.add("/user/register");
        writeOrder.add("/user/login");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        if (writeOrder.contains(httpServletRequest.getServletPath())) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        if (httpServletRequest.getServletPath().contains("/link/")) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        if (httpServletRequest.getServletPath().contains("/ics/get/")) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        String token = httpServletRequest.getHeader("token");
        //判断是否携带token
        if (StringUtils.isBlank(token)) {
            log.warn("no token!");
            httpServletRequest.setAttribute("error", "未验证用户");
            httpServletRequest.getRequestDispatcher("/error").forward(servletRequest, servletResponse);
            return;
        }
        //验证token
        try {
            myJWT.verify(token);
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (TokenExpiredException e) {
            //如果token超时.则redis中寻找
            if (Boolean.TRUE.equals(redisTemplate.hasKey(token))) {
                //从token中取出用户数据
                Map<String, Claim> userInfo = myJWT.getUserInfo(token);
                //生成新的token
                Map<String, Object> userMap = new HashMap<>();
                userMap.put("nickname", userInfo.get("nickname").asString());
                userMap.put("uid", userInfo.get("uid").asInt());
                String newToken = myJWT.create(userMap);
                // 删除redis中的token
                redisTemplate.delete(token);
                //将token保存到本地线程中
                threadLocalToken.setToken(newToken);
                //保存到redis中
                redisTemplate.opsForValue().set(newToken, "", 4, TimeUnit.DAYS);
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
                log.warn("令牌过期,请重新登录");
                httpServletRequest.setAttribute("error", "令牌过期,请重新登录");
                httpServletRequest.getRequestDispatcher("/error").forward(servletRequest, servletResponse);
            }
        }
    }
}
